#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QGridLayout>
#include <QPushButton>
#include <QHBoxLayout>
#include <QMessageBox>
#include <QLabel>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include <QTime>
#include <QColor>
#include <string>
#include <vector>
using namespace std;

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    //Init Attributes
    ui->setupUi(this);
    initialisation();
}

//Fonction pour nettoyer les grilles
void MainWindow::clear(){
    //Pour chaque neurones/cases passer à -1 (blanc)
    for(int i=0; i<LARGEUR; i++){
        for(int j=0; j<LARGEUR; j++){
            this->reseau.neurones[j+i*LARGEUR]=-1;
            neuronesTable[i][j]=-1;
        }
    }
    //Affichages
    printEnterCharacter(ui->tableWidEnter);
    printCurrentCharacter(ui->tableWidExit);
}

//Fonction qui initialise l'interface graphique et le réseau
void MainWindow::initialisation(){
    clear();

    /* PHASE 1 : INITIALISATION */
    int i;
    for(i=0;i<100;i++){
        megaApprentissage();
    }
    this->reseau.setCaractere(neuronesTable);
    printEnterCharacter(ui->tableWidEnter);

    //Set size of rows
    //For each cell
    int newCellSize=265/LARGEUR;
    for(int i=0; i<LARGEUR; i++){
        //Set new size
        ui->tableWidEnter->setRowHeight(i, newCellSize);
        ui->tableWidEnter->setColumnWidth(i, newCellSize);
        ui->tableWidExit->setRowHeight(i, newCellSize);
        ui->tableWidExit->setColumnWidth(i, newCellSize);
    }

    //Buttons action connect
    QObject::connect(ui->btn_launch, SIGNAL(clicked()), this, SLOT(launch()));
    QObject::connect(ui->btn_clear, SIGNAL(clicked()), this, SLOT(clear()));
    QObject::connect(ui->btn_zero, SIGNAL(clicked()), this, SLOT(toZero()));
    QObject::connect(ui->btn_un, SIGNAL(clicked()), this, SLOT(toOne()));
    QObject::connect(ui->btn_deux, SIGNAL(clicked()), this, SLOT(toTwo()));
    QObject::connect(ui->btn_trois, SIGNAL(clicked()), this, SLOT(toThree()));
    QObject::connect(ui->btn_quatre, SIGNAL(clicked()), this, SLOT(toFour()));
    QObject::connect(ui->btn_cinq, SIGNAL(clicked()), this, SLOT(toFive()));
    QObject::connect(ui->btn_six, SIGNAL(clicked()), this, SLOT(toSix()));
    QObject::connect(ui->btn_sept, SIGNAL(clicked()), this, SLOT(toSeven()));
    QObject::connect(ui->btn_huit, SIGNAL(clicked()), this, SLOT(toHuit()));
    QObject::connect(ui->btn_neuf, SIGNAL(clicked()), this, SLOT(toNine()));
    //QObject::connect(ui->getMat, SIGNAL(clicked()), this, SLOT(printCharacterToMatrice()));
    QObject::connect(ui->tableWidEnter, SIGNAL(cellClicked(int,int)), this, SLOT(changeCellValue(int,int)));
}

//Lancer la reconnaissance
void MainWindow::launch(){

    /* PHASE 2 : RECONNAISSANCE */
    this->reseau.setCaractere(neuronesTable);
    this->printEnterCharacter(ui->tableWidEnter);

    int i =0;
    float cumulCur=0;
    float oldCumul=-1;
    while(cumulCur != oldCumul){
        reseau.transformer();
        //reseau.afficherCaractereCourant();
        oldCumul=cumulCur;
        cumulCur = reseau.energie();
        i++;
    }
    
    calculErreurs();
    printCurrentCharacter(ui->tableWidExit);
}


//Fonction qui définit les pattern appris
void MainWindow::megaApprentissage(){
    // 0,1,2,3
    
    reseau.setCaractere(zero); reseau.apprentissage();
    reseau.setCaractere(un); reseau.apprentissage();
    reseau.setCaractere(deux);reseau.apprentissage();
    reseau.setCaractere(trois); reseau.apprentissage();
    reseau.setCaractere(quatre); reseau.apprentissage();
    //reseau.setCaractere(cinq); reseau.apprentissage();
    //reseau.setCaractere(six);reseau.apprentissage();
    //reseau.setCaractere(sept);reseau.apprentissage();
    //reseau.setCaractere(huit); reseau.apprentissage();
    //reseau.setCaractere(neuf); reseau.apprentissage();
}

//Calcul des erreurs
void MainWindow::calculErreurs(){
    
    int bestCaractere[LARGEUR][HAUTEUR];
    float best = 0.0;
    float current = 0.0;
    int caractere = 0;
    
    current = calculErreurCaractere(zero);
    printf("zero = %0.2f\n", current);
    if(current > best){
        caractere = 0;
        best = current;
    }
    current = calculErreurCaractere(un);
    printf("un = %0.2f\n", current);
    if(current > best){
        caractere = 1;
        best = current;
    }
    current = calculErreurCaractere(deux);
    printf("deux = %0.2f\n", current);
    if(current > best){
        caractere = 2;
        best = current;
    }
    current = calculErreurCaractere(trois);
    printf("Trois = %0.2f\n", current);
    if(current > best){
        caractere = 3;
        best = current;
    }
    current = calculErreurCaractere(quatre);
    printf("quatre = %0.2f\n", current);
    if(current > best){
        caractere = 4;
        best = current;
    }
    current = calculErreurCaractere(cinq);
    printf("cinq = %0.2f\n", current);
    if(current > best){
        caractere = 5;
        best = current;
    }
    current = calculErreurCaractere(six);
    printf("six = %0.2f\n", current);
    if(current > best){
        caractere = 6;
        best = current;
    }
    current = calculErreurCaractere(sept);
    printf("sept = %0.2f\n", current);
    if(current > best){
        caractere = 7;
        best = current;
    }
    current = calculErreurCaractere(huit);
    printf("huit = %0.2f\n", current);
    if(current > best){
        caractere = 8;
        best = current;
    }
    current = calculErreurCaractere(neuf);
    printf("neuf = %0.2f\n", current);
    if(current > best){
        caractere = 9;
        best = current;
    }
    
    
    switch (caractere) {
        case 0: this->reseau.setCaractere(zero); break;
        case 1: this->reseau.setCaractere(un); break;
        case 2: this->reseau.setCaractere(deux); break;
        case 3: this->reseau.setCaractere(trois); break;
        case 4: this->reseau.setCaractere(quatre); break;
        case 5: this->reseau.setCaractere(cinq); break;
        case 6: this->reseau.setCaractere(six); break;
        case 7: this->reseau.setCaractere(sept); break;
        case 8: this->reseau.setCaractere(huit); break;
        case 9: this->reseau.setCaractere(neuf); break;
        default: break;
    }
    
    
}

float MainWindow::calculErreurCaractere(int caractere[LARGEUR][HAUTEUR]){

    int i,j;
    float sameCell = 0;
    float totalCell = LARGEUR * HAUTEUR;
    
    for(i=0; i<LARGEUR; i++){
        for(j=0; j<HAUTEUR; j++){
            if(this->reseau.neurones[j+i*LARGEUR] == caractere[i][j] && this->reseau.neurones[j+i*LARGEUR] == 1){
                sameCell++;
            }
        }
    }
   
    return (sameCell/totalCell);
    
}


//Change la valeur (inverse) d'un neurone cliqué
void MainWindow::changeCellValue(int x, int y){
    int value=neuronesTable[x][y];
    if(value==-1){
        neuronesTable[x][y]=1;
    }else if(value==1){
        neuronesTable[x][y]=-1;
    }else{
        neuronesTable[x][y]=-1;
    }
    showCellEnter(x,y, ui->tableWidEnter,value);
    printEnterCharacter(ui->tableWidEnter);
}

//Print a cell in qtablewidget
void MainWindow::showCellEnter(int x, int y, QTableWidget *table, int value){
    QTableWidgetItem *item = new QTableWidgetItem();
    if(value==1){
        item->setBackgroundColor(QColor(0,0,0));
    }else{//morte
        item->setBackgroundColor(QColor(255,255,255));
    }
    item->setFlags(item->flags() & ~Qt::ItemIsEditable & ~Qt::ItemIsSelectable);
    table->setItem(x,y,item);
}


//Carac vers matrice
void MainWindow::printCharacterToMatrice(){
    int i,j;
    std::string s1;

    printf("{");
    for(i=0; i<LARGEUR; i++){
        printf("{");
        for(j=0; j<HAUTEUR; j++){
            int value = this->neuronesTable[i][j];
            if(j!=0){
                printf(",", value);
            }
            printf("%d", value);
        }
        printf("}");
        if(i!=LARGEUR-1){
            printf(",");
        }
    }
    printf("}\n\n");
}

//Affiche un caractère dans l'UI
void MainWindow::printCharacter(int caractere[LARGEUR][HAUTEUR], QTableWidget *table){
    int i,j;
    for(i=0; i<LARGEUR; i++){
        for(j=0; j<HAUTEUR; j++){
            int value = caractere[i][j];
            showCellEnter(i,j, table, value);
        }
    }
    table->repaint();
}

//Affiche le caractère obtenu en sortie dans l'UI
void MainWindow::printCurrentCharacter(QTableWidget *table){
    //vecteur de neurones to matrice caractere
    int caractere[LARGEUR][HAUTEUR] ;

    int largeurCount=0;
    int hauteurCount=0;
    for(int i=0; i<LARGEUR*HAUTEUR; i++){
        caractere[hauteurCount][largeurCount]=this->reseau.neurones[i];
        largeurCount++;
        if(largeurCount==LARGEUR){
            largeurCount=0;
            hauteurCount++;
        }
    }
    //affichage du caractere
    this->printCharacter(caractere, table);
}

//Affiche le caractère mis en entrée
void MainWindow::printEnterCharacter(QTableWidget *table){

    //affichage du caractere
    this->printCharacter(neuronesTable, table);
}


//Met un zéro en entrée
void MainWindow::toZero(){
    //Set neurones table tmp value
    for(int i=0; i< LARGEUR; i++){
        for(int j=0; j<LARGEUR; j++){
            neuronesTable[i][j]=zero[i][j];
        }
    }
    //Print
    printEnterCharacter(ui->tableWidEnter);
}

//Met un un en entrée
void MainWindow::toOne(){
    //Set neurones table tmp value
    for(int i=0; i< LARGEUR; i++){
        for(int j=0; j<LARGEUR; j++){
            neuronesTable[i][j]=un[i][j];
        }
    }
    //Print
    printEnterCharacter(ui->tableWidEnter);
}

//Met un deux en entrée
void MainWindow::toTwo(){
    //Set neurones table tmp value
    for(int i=0; i< LARGEUR; i++){
        for(int j=0; j<LARGEUR; j++){
            neuronesTable[i][j]=deux[i][j];
        }
    }
    //Print
    printEnterCharacter(ui->tableWidEnter);
}

//Met un trois en entrée
void MainWindow::toThree(){
    //Set neurones table tmp value
    for(int i=0; i< LARGEUR; i++){
        for(int j=0; j<LARGEUR; j++){
            neuronesTable[i][j]=trois[i][j];
        }
    }
    //Print
    printEnterCharacter(ui->tableWidEnter);
}

//Met un quatre en entrée
void MainWindow::toFour(){
    //Set neurones table tmp value
    for(int i=0; i< LARGEUR; i++){
        for(int j=0; j<LARGEUR; j++){
            neuronesTable[i][j]=quatre[i][j];
        }
    }
    //Print
    printEnterCharacter(ui->tableWidEnter);
}

//Met un cinq en entrée
void MainWindow::toFive(){
    //Set neurones table tmp value
    for(int i=0; i< LARGEUR; i++){
        for(int j=0; j<LARGEUR; j++){
            neuronesTable[i][j]=cinq[i][j];
        }
    }
    //Print
    printEnterCharacter(ui->tableWidEnter);
}

//Met un six en entrée
void MainWindow::toSix(){
    //Set neurones table tmp value
    for(int i=0; i< LARGEUR; i++){
        for(int j=0; j<LARGEUR; j++){
            neuronesTable[i][j]=six[i][j];
        }
    }
    //Print
    printEnterCharacter(ui->tableWidEnter);
}

//Met un sept en entrée
void MainWindow::toSeven(){
    //Set neurones table tmp value
    for(int i=0; i< LARGEUR; i++){
        for(int j=0; j<LARGEUR; j++){
            neuronesTable[i][j]=sept[i][j];
        }
    }
    //Print
    printEnterCharacter(ui->tableWidEnter);
}

//Met un huit en entrée
void MainWindow::toHuit(){
    //Set neurones table tmp value
    for(int i=0; i< LARGEUR; i++){
        for(int j=0; j<LARGEUR; j++){
            neuronesTable[i][j]=huit[i][j];
        }
    }
    //Print
    printEnterCharacter(ui->tableWidEnter);
}

//Met un neuf en entrée
void MainWindow::toNine(){
    //Set neurones table tmp value
    for(int i=0; i< LARGEUR; i++){
        for(int j=0; j<LARGEUR; j++){
            neuronesTable[i][j]=neuf[i][j];
        }
    }
    //Print
    printEnterCharacter(ui->tableWidEnter);
}

MainWindow::~MainWindow()
{
    delete ui;
}
